summaryrefslogtreecommitdiff
path: root/apps/mobile/app/task/[id].tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-18 02:58:27 +0000
committersoryu <soryu@soryu.co>2026-01-18 02:58:27 +0000
commitfcf9e70d54bd737d2dea848d25314120f37db503 (patch)
treebc304a9e153485f7686830614b2ddae4d4ff182e /apps/mobile/app/task/[id].tsx
parentf84a7f2d820f6f432be2b1d78d6bf833b5b19380 (diff)
downloadsoryu-fcf9e70d54bd737d2dea848d25314120f37db503.tar.gz
soryu-fcf9e70d54bd737d2dea848d25314120f37db503.zip
[WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC
Diffstat (limited to 'apps/mobile/app/task/[id].tsx')
-rw-r--r--apps/mobile/app/task/[id].tsx259
1 files changed, 259 insertions, 0 deletions
diff --git a/apps/mobile/app/task/[id].tsx b/apps/mobile/app/task/[id].tsx
new file mode 100644
index 0000000..121063a
--- /dev/null
+++ b/apps/mobile/app/task/[id].tsx
@@ -0,0 +1,259 @@
+import React from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ useColorScheme,
+ ActivityIndicator,
+} from 'react-native';
+import { useLocalSearchParams, Stack } from 'expo-router';
+import { Colors } from '../../constants/Colors';
+import { useTask } from '../../hooks/useTasks';
+import { TaskStatusBadge } from '../../components/TaskStatusBadge';
+import { EmptyState } from '../../components/EmptyState';
+
+export default function TaskDetailScreen() {
+ const colorScheme = useColorScheme() ?? 'light';
+ const colors = Colors[colorScheme];
+ const { id } = useLocalSearchParams<{ id: string }>();
+
+ const { data: task, isLoading, isError } = useTask(id);
+
+ if (isLoading) {
+ return (
+ <>
+ <Stack.Screen options={{ title: 'Loading...' }} />
+ <View style={[styles.container, styles.centered, { backgroundColor: colors.background }]}>
+ <ActivityIndicator size="large" color={colors.tint} />
+ </View>
+ </>
+ );
+ }
+
+ if (isError || !task) {
+ return (
+ <>
+ <Stack.Screen options={{ title: 'Error' }} />
+ <View style={[styles.container, { backgroundColor: colors.background }]}>
+ <EmptyState
+ icon="alert-circle-outline"
+ title="Failed to load task"
+ message="The task could not be found or an error occurred"
+ />
+ </View>
+ </>
+ );
+ }
+
+ return (
+ <>
+ <Stack.Screen options={{ title: task.name }} />
+ <ScrollView
+ style={[styles.container, { backgroundColor: colors.background }]}
+ contentContainerStyle={styles.content}
+ >
+ {/* Header */}
+ <View style={[styles.header, { backgroundColor: colors.card }]}>
+ <View style={styles.headerTop}>
+ <TaskStatusBadge status={task.status} showLabel size="large" />
+ </View>
+ <Text style={[styles.taskName, { color: colors.text }]}>
+ {task.name}
+ </Text>
+ {task.description && (
+ <Text style={[styles.description, { color: colors.secondaryText }]}>
+ {task.description}
+ </Text>
+ )}
+ </View>
+
+ {/* Progress Summary */}
+ {task.progressSummary && (
+ <View style={[styles.section, { backgroundColor: colors.card }]}>
+ <Text style={[styles.sectionTitle, { color: colors.text }]}>
+ Progress
+ </Text>
+ <Text style={[styles.progressText, { color: colors.secondaryText }]}>
+ {task.progressSummary}
+ </Text>
+ </View>
+ )}
+
+ {/* Task Info */}
+ <View style={[styles.section, { backgroundColor: colors.card }]}>
+ <Text style={[styles.sectionTitle, { color: colors.text }]}>
+ Details
+ </Text>
+
+ <View style={styles.infoRow}>
+ <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+ Created
+ </Text>
+ <Text style={[styles.infoValue, { color: colors.text }]}>
+ {new Date(task.createdAt).toLocaleString()}
+ </Text>
+ </View>
+
+ {task.startedAt && (
+ <View style={styles.infoRow}>
+ <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+ Started
+ </Text>
+ <Text style={[styles.infoValue, { color: colors.text }]}>
+ {new Date(task.startedAt).toLocaleString()}
+ </Text>
+ </View>
+ )}
+
+ {task.completedAt && (
+ <View style={styles.infoRow}>
+ <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+ Completed
+ </Text>
+ <Text style={[styles.infoValue, { color: colors.text }]}>
+ {new Date(task.completedAt).toLocaleString()}
+ </Text>
+ </View>
+ )}
+
+ {task.repositoryUrl && (
+ <View style={styles.infoRow}>
+ <Text style={[styles.infoLabel, { color: colors.secondaryText }]}>
+ Repository
+ </Text>
+ <Text
+ style={[styles.infoValue, { color: colors.tint }]}
+ numberOfLines={1}
+ >
+ {task.repositoryUrl}
+ </Text>
+ </View>
+ )}
+ </View>
+
+ {/* Subtasks */}
+ {task.subtasks && task.subtasks.length > 0 && (
+ <View style={[styles.section, { backgroundColor: colors.card }]}>
+ <Text style={[styles.sectionTitle, { color: colors.text }]}>
+ Subtasks ({task.subtasks.length})
+ </Text>
+ {task.subtasks.map((subtask) => (
+ <View key={subtask.id} style={styles.subtaskRow}>
+ <TaskStatusBadge status={subtask.status} size="small" />
+ <Text
+ style={[styles.subtaskName, { color: colors.text }]}
+ numberOfLines={1}
+ >
+ {subtask.name}
+ </Text>
+ </View>
+ ))}
+ </View>
+ )}
+
+ {/* Error message */}
+ {task.errorMessage && (
+ <View style={[styles.section, styles.errorSection]}>
+ <Text style={[styles.sectionTitle, { color: '#991b1b' }]}>
+ Error
+ </Text>
+ <Text style={styles.errorText}>{task.errorMessage}</Text>
+ </View>
+ )}
+
+ {/* Placeholder for future features */}
+ <View style={styles.placeholder}>
+ <Text style={[styles.placeholderText, { color: colors.secondaryText }]}>
+ Task output and controls will be added here
+ </Text>
+ </View>
+ </ScrollView>
+ </>
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ centered: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ content: {
+ padding: 16,
+ gap: 16,
+ },
+ header: {
+ padding: 16,
+ borderRadius: 12,
+ gap: 8,
+ },
+ headerTop: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ },
+ taskName: {
+ fontSize: 20,
+ fontWeight: '700',
+ },
+ description: {
+ fontSize: 14,
+ lineHeight: 20,
+ },
+ section: {
+ padding: 16,
+ borderRadius: 12,
+ gap: 12,
+ },
+ sectionTitle: {
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ progressText: {
+ fontSize: 14,
+ lineHeight: 20,
+ },
+ infoRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ infoLabel: {
+ fontSize: 14,
+ },
+ infoValue: {
+ fontSize: 14,
+ fontWeight: '500',
+ flex: 1,
+ textAlign: 'right',
+ marginLeft: 16,
+ },
+ subtaskRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 8,
+ paddingVertical: 4,
+ },
+ subtaskName: {
+ fontSize: 14,
+ flex: 1,
+ },
+ errorSection: {
+ backgroundColor: '#fee2e2',
+ },
+ errorText: {
+ fontSize: 14,
+ color: '#991b1b',
+ lineHeight: 20,
+ },
+ placeholder: {
+ padding: 32,
+ alignItems: 'center',
+ },
+ placeholderText: {
+ fontSize: 14,
+ textAlign: 'center',
+ },
+});